use crate::context::CONTEXT;
use crate::model::version::VersionItem;
use color_eyre::Result;
use crossterm::event::KeyCode::Esc;
use crossterm::event::{self, Event, KeyCode, KeyEventKind};
use ratatui::layout::Constraint::{Fill, Percentage};
use ratatui::{prelude::*, style::palette::tailwind, widgets::*};
use rust_i18n::t;
use std::fmt::Debug;
use std::io::{self};
use std::time::{Duration, Instant};

#[derive(Debug, Default, Clone)]
pub(crate) struct SearchApp<'a> {
    state: SearchAppState,
    list_state: ListState,
    list_version: Vec<VersionItem<'a>>,
    tips: String,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
enum SearchAppState {
    #[default]
    Running,
    Quit,
}

impl<'a> SearchApp<'a> {
    pub(crate) fn new(versions: Vec<VersionItem<'a>>) -> Self {
        let mut app = Self::default();
        app.list_version = versions;
        app
    }
    pub(crate) fn run(&mut self) -> Result<()> {
        let mut terminal = CONTEXT.get_terminal();
        self.draw(&mut terminal)?;
        while self.is_running() {
            self.handle_events()?;
            self.draw(&mut terminal)?;
        }
        Ok(())
    }

    fn is_running(&self) -> bool {
        self.state == SearchAppState::Running
    }

    fn draw(&mut self, terminal: &mut Terminal<impl Backend>) -> io::Result<()> {
        terminal.draw(|frame| frame.render_widget(self, frame.area()))?;
        Ok(())
    }

    fn handle_events(&mut self) -> Result<()> {
        use KeyCode::*;
        match event::read()? {
            Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
                Char('q') | Esc => self.quit(),
                Char('j') | Down => self.list_state.select_next(),
                Char('k') | Up => self.list_state.select_previous(),
                Enter => {
                    let selected = self.list_state.selected();
                    if let Some(index) = selected {
                        let Some(item) = self.list_version.get(index) else {
                            return Ok(());
                        };
                        if item.exist() {
                            return Ok(());
                        }
                        DownloadApp::new(item.clone()).run()?;
                        self.quit();
                    }
                }
                _ => (),
            },
            _ => {}
        }
        Ok(())
    }

    fn quit(&mut self) {
        self.state = SearchAppState::Quit;
    }

    #[allow(dead_code)]
    fn op_tips(&self) -> impl Widget {
        let palette = tailwind::SLATE;
        let fg = palette.c900;
        let bg = palette.c300;
        let keys_fg = palette.c50;
        let keys_bg = palette.c600;
        Line::from(vec![
            t!("app_name").into(),
            format!(
                "  ↓ | ↑ | {} | {} | {} | {}  ",
                t!("page_down"),
                t!("page_up"),
                t!("home"),
                t!("end"),
            )
            .fg(keys_fg)
            .bg(keys_bg),
            t!("quit").into(),
            "Esc".to_string().fg(keys_fg).bg(keys_bg),
            "    ".into(),
            t!("author").into(),
        ])
        .style((fg, bg))
    }

    fn line_numbers(&self, height: u16) -> impl Widget {
        let line_numbers = (1..=height)
            .map(|n| format!("{n:>4} \n"))
            .collect::<String>();
        Text::from(line_numbers).dim()
    }

    fn list(&self) -> impl StatefulWidget<State = ListState> {
        let block = Block::bordered().title("List");
        let mut items = vec![];
        self.list_version.iter().for_each(|item| {
            let exist = if item.exist() { "✔️" } else { "🪄" };
            let lts = if item.lts() { "lts" } else { "   " };
            items.push(ListItem::new(format!(
                " {} {}  {}  {}  {}  {}",
                exist,
                item.name(),
                item.version(),
                lts,
                item.size(),
                item.channel().unwrap_or("")
            )));
        });

        List::new(items)
            .block(block)
            .highlight_style(Style::new().blue())
            .highlight_symbol("👉")
            .repeat_highlight_symbol(true)
    }
}

const BLOCK_HEIGHT: u16 = 20;

impl Widget for &mut SearchApp<'_> {
    fn render(self, _area: Rect, buf: &mut Buffer) {
        let mut lines = BLOCK_HEIGHT;
        match term_size::dimensions() {
            Some((_width, height)) => {
                lines = height as u16;
            }
            None => {
                lines = BLOCK_HEIGHT;
            }
        }
        let _width = if buf.area.height < lines {
            buf.area.width - 1
        } else {
            buf.area.width
        };
        use Constraint::*;
        let area = buf.area;
        let [numbers, widgets] = Layout::horizontal([Length(5), Fill(1)]).areas(area);
        let [list, bottom_rect] = Layout::vertical([Fill(1), Length(2)]).areas(widgets);
        self.line_numbers(area.height).render(numbers, buf);
        self.list().render(list, buf, &mut self.list_state);
        let span = Span::raw(&self.tips).style(Style::default().red());
        let bottom_bar = Block::default()
            .title(block::Title::from("Tips").alignment(Alignment::Right))
            .borders(Borders::ALL);
        bottom_bar.render(bottom_rect, buf);
        Paragraph::new(span).render(bottom_rect, buf);
    }
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
enum DownloadAppState {
    #[default]
    Running,
    Quit,
}

#[derive(Debug)]
struct DownloadApp<'a> {
    state: DownloadAppState,
    percent: u16,
    version_item: VersionItem<'a>,
    start_time: Instant,
}

impl<'a> DownloadApp<'a> {
    fn default() -> Self {
        DownloadApp {
            state: Default::default(),
            percent: 0,
            version_item: Default::default(),
            start_time: Instant::now(),
        }
    }
    pub(crate) fn new(version_item: VersionItem<'a>) -> Self {
        let mut app = Self::default();
        app.version_item = version_item;
        app
    }

    fn handle_events(&mut self) -> Result<()> {
        if event::poll(Duration::from_millis(100))? {
            match event::read()? {
                Event::Key(key) if key.kind == KeyEventKind::Press => {
                    match (key.modifiers, key.code) {
                        (event::KeyModifiers::CONTROL, KeyCode::Char('c')) => {
                            self.quit();
                        }
                        _ => (),
                    }
                }
                Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
                    Esc => self.quit(),
                    _ => (),
                },
                _ => {}
            }
        }
        Ok(())
    }

    pub(crate) fn is_running(&mut self) -> bool {
        self.state == DownloadAppState::Running
    }
    pub(crate) fn run(&mut self) -> Result<()> {
        let mut terminal = CONTEXT.get_terminal();
        self.draw(&mut terminal)?;
        while self.is_running() {
            self.handle_events()?;
            self.check_download();
            self.draw(&mut terminal)?;
        }
        Ok(())
    }

    //超时判断

    /// 下载状态监测
    fn check_download(&mut self) {
        self.percent = self.percent + 1;
        // if self.percent > 90 {
        //     self.percent = 1
        // }
        let now = Instant::now();
        let elapsed = now.duration_since(self.start_time);
        if elapsed.as_secs() > 600 {
            println!("下载超时");
            self.quit();
        }
        if self.percent > 99 {
            println!("下载完成");
            self.quit();
        }
    }

    fn draw(&mut self, terminal: &mut Terminal<impl Backend>) -> io::Result<()> {
        terminal.draw(|frame| frame.render_widget(self, frame.area()))?;
        terminal.swap_buffers();
        Ok(())
    }
    fn quit(&mut self) {
        self.state = DownloadAppState::Quit;
    }

    fn download_progress(&self) -> impl Widget {
        let block = Block::bordered().title("Download");
        let info = format!(
            "{} {} {:.1}%",
            self.version_item.name(),
            self.version_item.version(),
            self.percent
        );
        let progress = Gauge::default()
            .block(block)
            .label(info)
            .bg(Color::Blue)
            .fg(Color::LightBlue)
            .gauge_style(Style::new().light_blue().on_blue())
            .percent(self.percent);
        progress
    }
}

impl Widget for &mut DownloadApp<'_> {
    fn render(self, _area: Rect, buf: &mut Buffer) {
        let _width = if buf.area.height < 20 {
            buf.area.width - 1
        } else {
            buf.area.width
        };
        let area = buf.area;
        let [_, block, _] = Layout::vertical([Percentage(40), Fill(1), Percentage(40)]).areas(area);
        let [_, progress, _] = Layout::horizontal([Fill(1), Percentage(90), Fill(1)]).areas(block);
        self.download_progress().render(progress, buf);
    }
}
